﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace LX.Core.Query
{
    public  class EntityFilter<T>
    {
        public static EntityFilter<T> Empty
        {
            get { return new EntityFilter<T>(); }
        }

        public Expression<Func<T, bool>> WhereCodition { get; set; }
        public PageCodition PageCodition { get; set; }
        public List<OrderCodition> OrderCodition { get; set; }

        public void Where(Expression<Func<T, bool>> exp)
        {
            WhereCodition = exp;
        }

        public void PageTo(int page,int pagesize)
        {
            this.PageCodition = new PageCodition {Page = page, PageSize = pagesize};
        }

        public void OrderBy<Tresult>(Expression<Func<T, Tresult>> exp)
        {
            //var expbody = exp.Body;
            //MemberExpression memberexp = (MemberExpression) expbody;
            //var name = memberexp.Member.Name;
            if (OrderCodition == null)
            {
                OrderCodition = new List<OrderCodition>();
            }
            OrderCodition.Add(new OrderCodition { Expression = exp, OrderType =  OrderType.Asc });
        }

        public void OrderBy(string filed)
        {
            //var expbody = exp.Body;
            //MemberExpression memberexp = (MemberExpression) expbody;
            //var name = memberexp.Member.Name;
            if (OrderCodition == null)
            {
                OrderCodition = new List<OrderCodition>();
            }
             Type type = typeof(T);
            var att = type.GetProperty(filed, System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public);
            if (att != null)
            {
                System.Linq.Expressions.ParameterExpression par = System.Linq.Expressions.Expression.Parameter(type);
                System.Linq.Expressions.MemberExpression memberaccess = System.Linq.Expressions.Expression.MakeMemberAccess(par, att);
                System.Linq.Expressions.LambdaExpression exp = System.Linq.Expressions.Expression.Lambda(memberaccess, par);
                OrderCodition.Add(new OrderCodition { Expression = exp, OrderType = OrderType.Asc });
            }
            
        }

        public void OrderByDescending<Tresult>(Expression<Func<T, Tresult>> exp)
        {
            if (OrderCodition == null)
            {
                OrderCodition = new List<OrderCodition>();
            }
            OrderCodition.Add(new OrderCodition { Expression = exp, OrderType = OrderType.Desc });
        }

        public void OrderByDescending(string filed)
        {
            //var expbody = exp.Body;
            //MemberExpression memberexp = (MemberExpression) expbody;
            //var name = memberexp.Member.Name;
            if (OrderCodition == null)
            {
                OrderCodition = new List<OrderCodition>();
            }
            Type type = typeof(T);
            var att = type.GetProperty(filed, System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public);
            if (att != null)
            {
                System.Linq.Expressions.ParameterExpression par = System.Linq.Expressions.Expression.Parameter(type);
                System.Linq.Expressions.MemberExpression memberaccess = System.Linq.Expressions.Expression.MakeMemberAccess(par, att);
                System.Linq.Expressions.LambdaExpression exp = System.Linq.Expressions.Expression.Lambda(memberaccess, par);
                OrderCodition.Add(new OrderCodition { Expression = exp, OrderType = OrderType.Desc });
            }

        }

        public IQueryable<T> Filter(IQueryable<T> source)
        {
            if(WhereCodition!=null)
            {
                source = source.Where(WhereCodition);
            }
            if(OrderCodition!=null&&OrderCodition.Count>0)
            {
                source = QueryOrder(source);
            }
            source=QueryPage(source);
            return source;
        }

        public int Count(IQueryable<T> source)
        {
            if (WhereCodition != null)
            {
                source = source.Where(WhereCodition);
            }
            return source.Count();
        }

        private IQueryable<T> QueryPage(IQueryable<T> query)
        {
            if (query == null || PageCodition == null || PageCodition.Page <= 0 || PageCodition.PageSize <= 0) return query;
            if (PageCodition.Page > 1)
            {
                query = query.Skip(PageCodition.Page * PageCodition.PageSize);
            }
            return query.Take(PageCodition.PageSize);
        }

        private IQueryable<T> QueryOrder(IQueryable<T> query)
        {
            var orders = OrderCodition.Reverse<OrderCodition>();
            foreach (var ocd in orders)
            {
                if (ocd.Expression == null) continue;
                LambdaExpression exp = ocd.Expression as LambdaExpression;
                if (exp == null) continue;
                MemberExpression memberexp = exp.Body as MemberExpression;
                if (memberexp == null) continue;
                String ordertype = "OrderBy";
                if (ocd.OrderType == OrderType.Asc)
                {
                    ordertype = "OrderBy";
                }
                else
                {
                    ordertype = "OrderByDescending";
                }
                //memberexp.Member.ReflectedType
                var p = (PropertyInfo) memberexp.Member;
                Type t = p.PropertyType;
                Type[] typeArgs = new Type[] { query.ElementType, t };
                var mc = Expression.Call(typeof(Queryable), ordertype, typeArgs, query.Expression, ocd.Expression);
                query = query.Provider.CreateQuery<T>(mc);
            }
            return query;
        }

        
    }
}
